跳到主要内容

API

接口待重构

调用远程服务会有3种结果:成功、失败与超时。

  • 成功时会返回该请求的返回值,失败时则会返回错误码和错误信息。
  • 注意,超时发生后,依然可能会有成功或失败事件被触发(比如刚超时就成功了。而我们不会因为超时就把一次成功的操作给回滚)。
  • 当然,如果对应的远程服务遇到了崩溃,那么就会遇到只有超时,但没有成功或失败。
  • 所以,超时通常只是脚本流程的一个保险,避免流程进行不下去。理论上我们会尽量避免超时发生。
  • 通过注册okerrortimeout事件可以获取请求结果。
    • 当然,如果你不关心请求结果,你可以不注册这些事件。

官方大厅图有读写其它图的各信息的权限(否则游戏的战报信息大厅就拿不到了)

积分、存档、道具、列表、货币名字这几套会有统一的commit,以便消耗道具、修改积分、修改列表在同一次commit里;统计没有commit,操作了立即生效

  • 只要在一次commit里提交的,都是一个事务,如有意外的话本次commit里的所有数据操作都将一起失败
  • 不同系统里的索引(key)允许重复。甚至普通积分和数字积分也允许有重复的key
  • 只要游戏结束前调用commit就算有效,即使在提交积分的时间里游戏已经结束。不过游戏结束后lua就收不到提交的反馈了,你只能希望他成功了
  • 用户已经开启了游戏局的,大厅局对玩家的积分修改会失效,所以务必在申请开始游戏局前把所有要改的积分都commit

所有的超时时间,如果没有特殊说明,都是5秒

一秒内不允许做超过65536个查询或commit操作,否则回调会错乱。

API大体和服务器的积分系列api相同, 删除了一些安全性敏感的api

所有的写操作的target_map都强制指定为sce.s.readwrite_map 对于只读操作, 允许读取sce.s.readwrite_mapsce.s.readonly_map这两个地图的数据

需要特别注意, 客户端是32bit的应用程序, 所以文档内的所有integer都是32bit的, 这与服务端不同!

通用接口

get_commit

获得一个提交对象。除了统计,所有的修改操作都必须在提交对象上进行。可以把有关联的,必须同时成功的修改操作放在同一个提交对象里。

这是一个异步接口(积分系列的api大部分都是异步接口),也就是说,调用commit之后立刻去获取积分,你很可能取不到正确的值。实际上,即使积分服瞬间返回(实际上多少会有少量的延迟),commit的回调最快也要等当前语境的lua代码全都被执行完,才会被调用。之前没有接触过异步接口的人,千万不要以为ok的回调会先执行

由c++实现的api


local c = sce.s.get_commit()

commit

结束之前获得的提交对象,真正提交所有修改。在commit成功之前,所有相关修改未必真正的写入了数据库。提交需要一定的事件,通过注册okerrortimeout事件获得提交的结果。

所有操作会强制作用于地图sce.s.readwrite_map

  • 参数
    • desc (string) - 本次提交的描述。和提交相关联,在事后通过后台查看某些积分字段的修改记录时可以看到。
    • events (table) - (可选) 响应事件

local c = sce.s.get_commit()
-- ...
-- 若干修改操作
-- ...
c.commit('比如转箱子',
{
ok = function ()
-- 提交成功
-- do some thing after success
end,
error = function (code, reason)
-- 提交失败
end,
timeout = function ()
-- 提交超时
end,
})
-- some other thing1
-- some other thing2
-- 不熟悉异步机制的请注意:some other thing1和some other thing2都会比do some thing after success先执行的


local c = sce.s.get_commit()
-- ...
-- 若干修改操作
-- ...
c.commit('增加地图专用货币', 'another_map',
{
ok = function ()
-- 提交成功
end,
error = function (code, reason)
-- 提交失败
end,
timeout = function ()
-- 提交超时
end,
})

score

积分

积分使用字符串作为索引(key),索引最长180。

积分分为普通积分、数字积分和字符串积分。数字积分必须是整数(int64范围),普通积分可以是任意类型,字符串,数字,表,都可以(但通常是用表)。对于普通积分来说,不管什么类型,均会在c++底层被序列化成二进制,二进制的长度不得超过64K。

普通积分、数字积分和字符串积分的索引允许重复。

将积分设置为nil可以删除该积分,当你决定不再使用某项积分后,可以删除它。

当对积分进行操作时,并不会直接修改副本,而是将操作记录下来。当使用提交积分的功能提交操作记录后,才会根据操作记录更新副本。

!> 玩家离开游戏后,依然可以设置他的积分。但你最好不要这样做,因为该玩家可能会新开一局游戏,然后新老游戏局的同索引积分就会冲突。

score_init

初始化(读取)某玩家的积分。这是一个异步接口(积分系列的api大部分是异步接口),你得收到回调后才能去访问积分的值。

理论上,你可以不读取积分直接对积分进行写。

  • 参数
    • score_name (string) - (可选)地图积分名。用来查其它地图的积分(得有权限才让查)(对应地图表里没对应配置的时候默认就是地图名)。如果第一个参数是字符串,则表示是地图积分名。
    • player (player/integer/nil) - 玩家。如果传nil,则表示使用客户端自己的userid。传integer的话则表示是userid。注意必须是大厅接口给返回的id,从Host接口返回的userid好像是虚拟的,这里不能用。如果传的是number,会被强转成integer;如果强转失败,则会当0用(假设userid < 1000 即为特殊userid, 可以记录一些全局数据, 比如记录boss被干掉了多少次,被干掉的次数越多会越强?)
      • 注意当player == nil时, 服务端与客户端的处理方式是不同
    • events (table) - 响应事件
    • 任意个key (string) - 要获取玩家的哪些key。如果一个key都不指定,则表示取该玩家的所有积分。

当积分初始化完成时,会根据初始化结果执行对应事件。如果使用该方法时积分已经初始化完毕,依然会再去数据库获取一次。并没有缓存,所以如无必要请避免反复调用该方法。

大厅服和匹配服的Lua脚本里可以获取到用户的部分积分,用于大厅和匹配的一些逻辑

由c++实现的api


-- 获取player的所有积分
sce.s.score_init(player,
{
ok = function (score, iscore, sscore)
-- 初始化完成。scores保存了普通积分,iscores保存了数字积分。
-- 有必要的话请自行保存这两个表
end,
error = function (code, reason)
-- 初始化失败
end,
timeout = function ()
-- 初始化超时
end,
})

--获取player2在AutoChess地图里的elo和ts这两项积分。不需要获取全部的情况下就少获取点,减轻服务器负担..
sce.s.score_init('AutoChess', player2,
{
ok = function (score, iscore, sscore)
-- 初始化完成。scores保存了普通积分,iscores保存了数字积分。
end,
error = function ()
-- 初始化失败
end,
timeout = function ()
-- 初始化超时
end,
}, 'elo', 'ts')

score_set

设置普通积分

  • 参数

    • player (player/integer/nil) - 玩家
    • key (string) - 索引
    • value (string/integer/table/nil) - 积分的值
  • 返回

    • ok (boolean) - 是否成功。目前失败的唯一可能就是table序列化后超过64K.

local c = sce.s.get_commit()
c.score_set(player, key, value)
-- .. 再改点别的什么
c.commit('描述')

score_seti

设置数字积分

  • 参数
    • player (player/integer/nil) - 玩家
    • key (string) - 索引
    • value (integer) - 积分的值

local c = sce.s.get_commit()
c.score_seti(player, key, value)
-- .. 再改点别的什么
c.commit('更新elo积分')

score_addi

累加数字积分。如果玩家的这项积分之前没有被设置过,那么假设之前是0。

  • 参数
    • player (player/integer/nil) - 玩家
    • key (string) - 索引
    • value (integer) - 积分的值

local c = sce.s.get_commit()
c.score_addi(player, key, value, map_name)
-- .. 再改点别的什么
c.commit('累加积分')

score_sets

设置字符串积分

  • 参数
    • player (player/integer/nil) - 玩家
    • key (string) - 索引
    • value (string) - 积分的值

local c = sce.s.get_commit()
c.score_sets(player, key, value)
-- .. 再改点别的什么
c.commit('更新elo积分')

money

货币

  • 有若干种平台通用货币,若干种地图自定义货币
    • 货币种类程序直接改数据库,后期api支持下,只允许后台访问
  • 人民币只能充值成钻石(平台通用的人民币对应货币),否则充值界面不好展示
    • 如果以后有单发特定地图的包,那可以只显示该地图的人民币对应货币(只能有一种人民币对应货币)
  • 查余额、扣费、加钱(人民币对应货币不能在游戏局里加,只能在后台里加)
  • 支持游戏内直接付钱(不转成货币)
    • PC端弹二维码,网页端新窗口弹链接,手机端必须从渠道充,没有直接充这一说
  • 和积分的区别,主要是有统一的充值扣款界面,可以用在统一做的商城里

money_init

初始化(读取)某玩家的所有货币的余额。货币初始化需要一段时间,请不要立即使用它。

理论上,你可以不读取货币余额,直接对该用户的货币进行增减。

由c++实现的api

  • 参数
    • player (player/integer/nil) - 玩家
    • events (table) - 响应事件

当货币初始化完成时,会根据初始化结果执行对应事件。如果使用该方法时货币已经初始化完毕,依然会再去数据库获取一次。并没有缓存,所以如无必要请避免反复调用该方法。


-- 获取player的所有货币的余额
sce.s.money_init(player,
{
ok = function (moneys)
-- 初始化完成
end,
error = function (code, reason)
-- 初始化失败
end,
timeout = function ()
-- 初始化超时
end,
})

money_add

加钱

  • 参数
    • player (player/integer/nil) - 玩家
    • money_name (string) - 货币的名字
    • value (integer) - 加多少钱

local c = sce.s.get_commit()
c.money_add(player, money_name, value)
-- .. 再改点别的什么
c.commit('红包领取')

money_cost

扣钱

  • 参数
    • player (player/integer/nil) - 玩家
    • money_name (string) - 货币的名字
    • value (integer) - 扣多少钱

local c = sce.s.get_commit()
c.money_cost(player, money_name, value)
-- .. 再改点别的什么
c.commit('买装备')

name

名字

  • 支持每地图每大区范围唯一起名 (没有大区概念前,就都默认在1区)
    • 不存在才允许插入键值对,否则失败
  • 支持以索引(key)的子串搜索
    • 暂不支持拼音首字母的搜索

搜索包含指定字符串的所有名字

由c++实现的api

  • 参数
    • key (string) - 名字的索引
    • name_substr (string) - 名字的子串
    • events (table) - 响应事件
  • 返回
    • names (table) - 一个数组。数组的每一项有两个字段:
      • name (string) - 名字。name必定包含name_substr。
      • value (string) - 当时调name_new时存进去的这个名字对应的值。
      • names表会按name比name_substr多的字符个数排序,和name_substr越是接近的越排前面。暂时写死了只返回15个

每次调用都会去数据库搜索


-- 获取索引是'nick'的这类名字里面,包含substr的名字
sce.s.name_search('nick', substr,
{
ok = function (names)
-- 查询完成
end,
error = function (code, reason)
-- 查询失败
end,
timeout = function ()
-- 查询超时
end,
})

list_add

列表新增项

  • 参数
    • player (player/integer/nil) - 玩家
    • key (string) - 列表的key
    • value (string/integer/table/nil) - 统计的值

local c = sce.s.get_commit()
c.list_add(player, key, value)
...
c.commit("列表新增")

list_modify

列表修改项

  • 参数
    • player (player/integer/nil) - 玩家
    • key (string) - 列表的key
    • list_id (integer) - list项的唯一标识id
    • value (string/integer/table/nil) - 目标value

local c = sce.s.get_commit()
c.list_modify(player, key, list_id, value)
...
c.commit("列表修改值")

list_delete

列表删除项

  • 参数
    • player (player/integer/nil) - 玩家
    • key (string) - 列表的key
    • list_id (integer) - list项的唯一标识id。注意每一项的id都是独立和互不干扰的,删除某一项不会对别的项的id有变化

local c = sce.s.get_commit()
c.list_delete(player, key, list_id)
...
c.commit("列表删除")

list_query

列表查询。返回的数据按时间的逆序排序。即晚插入的排前面。

由c++实现的api

  • 参数
    • map_name (string) - 要查询的地图名
    • player (player/integer/nil) - 玩家
    • key (string) - 列表的key
    • limit (integer) - (可选) 返回最多limit条记录。不填则表示返回所有
    • callback (table) - 和其它查询类api的callback类似,具体写法参考例子
    • timetype (string) - (可选) 如果这里传一个字符串stamp,则返回的time是一个以秒为单位的时间戳,类似1587524484这样。否则(默认情况),返回的是一个时间,类似2020-04-22 11:01:24
  • 返回
    • user_id (integer) - 玩家id
    • key (string) - 统计的key
    • value (string) - 统计的值
    • list_id (integer) - list项的唯一标识id。注意id会是个比较大的数,id不能通过加减来得到别的id
    • time (string) - 列表被插入的时间(或时间戳)。不管是时间戳还是时间,这里都是字符串类型。

sce.s.list_query(player, key, {
ok = function (result)
-- 查询完成
if #result > 0 then
for i, u in ipairs(result) do
print(u.user_id)
print(u.key)
print(u.value)
print(u.list_id)
end
end
end,
})

sce.s.list_query(player, key, {
ok = function (result)
-- 查询完成
if #result > 0 then
for i, u in ipairs(result) do
print(u.user_id)
print(u.key)
print(u.value)
print(u.list_id)
end
end
end,
}, 'stamp')

item

物品相关,现阶段用来做玩家背包的

query_item

查询用户所拥有的所有或者某一key类型的物品,user_id和item_id相同或者user_id,key,expire_type,expire_time相同都会被认为是同一物品

由c++实现的api

  • 参数
    • player (player/integer) - 玩家
    • callback (table) - 查询回调
    • key (string) - 可选参数,表示查询某一类物品
  • 返回
    • user_id (integer) - 玩家id
    • key (string) - 物品分类key
    • item_name (string) - 物品名
    • expire_type (integer) - 过期类型 0-永久,1-指定日期过期,2-指定时间过期
    • expire_time (string) - 过期时间,都是指定日期,指定时间入库时也换算成指定日期,对于永久类型的过期时间一定为9999-12-31 23:59:59
    • count (integer) - 物品数量
    • value (table) - 物品额外信息
    • item_id (integer) - 物品id

sce.s.query_item(player, {
ok = function (result)
-- 查询完成
if #result > 0 then
for i, u in ipairs(result) do
print(u.user_id)
print(u.key)
print(u.item_name)
print(u.expire_type)
print(u.expire_time)
print(u.count)
print(u.value)
print(u.item_id)
end
end
end,
}, 'key')

item_add

添加物品

  • 参数
    • player (player/integer) - 玩家
    • key (string) - 标识某一类物品
    • item_name (string) - 物品名
    • count (integer) - 物品数量
    • value (table) - 物品额外信息
    • expire_type (integer) - 过期类型 0-永久,1-指定日期过期,2-指定时间过期
    • expire_time (string) - 过期时间,都是指定日期,指定时间入库时也换算成指定日期,对于永久类型的过期时间一定为9999-12-31 23:59:59, 永久类型不需要填此参数

local c = sce.s.get_commit()
c.item_add(player, 'key', 'item_name0', 10, {}, 0)
c.item_add(player, 'key', 'item_name1', 10, {}, 1, '2020-3-4 20:12:12')
c.item_add(player, 'key', 'item_name2', 10, {}, 2, 1*60*60) // 一小时后过期,单位秒
c.commit('item add')

item_use

使用物品

  • 参数
    • player (player/integer) - 玩家
    • item_id (integer) - 物品id
    • count (integer) - 物品数量

local c = sce.s.get_commit()
c.item_use(player, item_id, 8)
c.commit('item use')